#include "pwext.hpp"
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/signal.h>
#include <setjmp.h>
#include <list>

// [[ex05ca]]
// DEBUG descr Setting up a long jump from the signal handler.
__thread jmp_buf onsig;
__thread volatile bool use_onsig = false;

void dummyhandler(int sig)
{
	if (use_onsig)
		longjmp(onsig, 1);
}
// END

// DEBUG from
struct Request
{
	bool isValid()
	{
		return valid_;
	}
	void handle()
	{ }

	bool valid_;
};

pw::semaphore started(0);

class oldcode {
public:
// DEBUG to
// [[ex05cb]]
// DEBUG descr The thread logic before adding the long jump support.
class worker : public pw::pwthread
{
// DEBUG { copied from ex05bd, added fd_
public:
	worker() : 
		exit_flag_(false), exit_confirm_flag_(false), fd_(0)
	{ }
	// ...
	void *execute()
	{
		while(!get_exit_flag()) {
			fprintf(stderr, "worker thread %p in the loop\n", this);
			started.signal();
			pw::event delay;
			delay.msecwait(10); // artificially increase the race window
			fprintf(stderr, "worker thread %p is about to read the request\n", this);
			Request r = get_next_request();

			if (get_exit_flag())
				break;
			if (!r.isValid())
				continue;

			// ... handle the request until need to sleep...
			if (mysleep(1000) != ETIMEDOUT)
				break; // it means, the exit was signaled
			// ... continue handling the request
		}
		{
			pw::lockmutex lm(exit_mutex_);
			exit_confirm_flag_ = true;
		}
		exit_confirm_event_.signal();
		fprintf(stderr, "worker thread %p exits\n", this);
	}

	void stop()
	{
		{
			pw::lockmutex lm(exit_mutex_);
			if (exit_confirm_flag_) // already stopped
				return;
			exit_flag_ = true;
		}
		exit_event_.signal();
		if (id_ != 0)
			pthread_kill(id_, SIGUSR1); // id_ is inherited from pw::thread
	}

	void stop_and_confirm()
	{
		{
			pw::lockmutex lm(exit_mutex_);
			exit_flag_ = true;
		}
		exit_event_.signal();
		do {
			if (id_ != 0)
				pthread_kill(id_, SIGUSR1); // id_ is inherited from pw::thread
			else
				break;
		} while(exit_confirm_event_.msecwait(1) == ETIMEDOUT);
	}

	bool has_exited()
	{
		pw::lockmutex lm(exit_mutex_);
		return exit_confirm_flag_;
	}

	// will return 0 if exit was signaled, ETIMEDOUT otherwise
	int mysleep(int milliseconds)
	{
		return exit_event_.msecwait(milliseconds);
	}

protected:
	bool get_exit_flag()
	{
		pw::lockmutex lm(exit_mutex_);
		return exit_flag_;
	}

	bool exit_flag_;
	bool exit_confirm_flag_;
	pw::pmutex exit_mutex_;
	pw::event exit_event_;
	pw::event exit_confirm_event_;
	// added specially for this example
	int fd_;
// DEBUG }
	// ...
protected:
	Request get_next_request()
	{
		Request r;

		if (exit_flag_) {
			r.valid_ = false;
			return r;
		}

		// restart after EINTR from the other signals
		int len;
		do {
			len = ::read(fd_, &r, sizeof r);
		} while (len < 0 && errno == EINTR && !exit_flag_);

		if (len != sizeof r) {
			r.valid_ = false;
			exit_flag_ = true;
			return r;
		}

		r.valid_ = true;
		return r;
	}
	// ...
};
// END
// DEBUG from
};

// [[ex05cc]]
// DEBUG descr A system call wrapper implementing the long jump support.
// will return -1 and errno==EINTR if interrupted by
// longjmp
int catchread(bool *exit_flag, int fd, char *buf, 
	int count)
{
	int len;
	if (setjmp(onsig)) {
		// gets here if the signal has called longjmp()
		use_onsig = false;
		errno = EINTR;
		return -1;
	} else {
	// DEBUG {
			fprintf(stderr, "worker thread in catchread\n");
			started.signal();
	// DEBUG }
		use_onsig = true; // the jump structure is set

		// re-check the exit flag after setting the
		// handler
		if (*exit_flag) {
			use_onsig = false;
			errno = EINTR;
			return -1;
		}

	// DEBUG {
			usleep(10*1000); // artificially increase the race window
			fprintf(stderr, "worker thread is about to read the request\n");
	// DEBUG }
		do {
			len = read(fd, buf, count);
		} while (len < 0 && errno == EINTR);

		use_onsig = false;
	}
	return len;
}
// END

// [[ex05cd]]
// DEBUG descr The thread logic relying on a long jump for interruption.
class worker : public pw::pwthread
{
// DEBUG { copied from ex05bd, added fd_, moved the delay
public:
	worker() : 
		exit_flag_(false), exit_confirm_flag_(false), fd_(0)
	{ }
	// ...
	void *execute()
	{
		while(!get_exit_flag()) {
			Request r = get_next_request();

			if (get_exit_flag())
				break;
			if (!r.isValid())
				continue;

			// ... handle the request until need to
			// sleep...
			if (mysleep(1000) != ETIMEDOUT)
				break; // it means, the exit was signaled
			// ... continue handling the request
		}
		{
			pw::lockmutex lm(exit_mutex_);
			exit_confirm_flag_ = true;
		}
		exit_confirm_event_.signal();
		fprintf(stderr, "worker thread %p exits\n", this);
	}

	void stop()
	{
		{
			pw::lockmutex lm(exit_mutex_);
			if (exit_confirm_flag_) // already stopped
				return;
			exit_flag_ = true;
		}
		exit_event_.signal();
		if (id_ != 0)
			pthread_kill(id_, SIGUSR1); // id_ is inherited from pw::thread
	}

	void stop_and_confirm()
	{
		{
			pw::lockmutex lm(exit_mutex_);
			exit_flag_ = true;
		}
		exit_event_.signal();
		do {
			if (id_ != 0)
				pthread_kill(id_, SIGUSR1); // id_ is inherited from pw::thread
			else
				break;
		} while(exit_confirm_event_.msecwait(1) == ETIMEDOUT);
	}

	bool has_exited()
	{
		pw::lockmutex lm(exit_mutex_);
		return exit_confirm_flag_;
	}

	// will return 0 if exit was signaled, ETIMEDOUT otherwise
	int mysleep(int milliseconds)
	{
		return exit_event_.msecwait(milliseconds);
	}

protected:
	bool get_exit_flag()
	{
		pw::lockmutex lm(exit_mutex_);
		return exit_flag_;
	}

	bool exit_flag_;
	bool exit_confirm_flag_;
	pw::pmutex exit_mutex_;
	pw::event exit_event_;
	pw::event exit_confirm_event_;
	// added specially for this example
	int fd_;
// DEBUG }
	// ...
protected:
	Request get_next_request()
	{
		Request r;

		int len = catchread(&exit_flag_, fd_, 
			(char *)&r, sizeof r);

		if (len != sizeof r) {
			r.valid_ = false;
			exit_flag_ = true;
			return r;
		}

		r.valid_ = true;
		return r;
	}
	// ...
};
// END

// DEBUG {
int main(int argc, char **argv)
{
	// ...
	struct sigaction sa;
	sa.sa_handler = dummyhandler;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = 0; // do not specify SA_RESTART !!!
	sigaction(SIGUSR1, &sa, NULL);
// ... in the main/stopper thread
std::list<worker *> workers;
std::list<worker *>::iterator it;
// ...
	const int NWORKERS = 3;
	for (int i = 0; i < NWORKERS; i++) {
		worker *t = new worker;
		workers.push_back(t);
	}
	for (it = workers.begin(); it != workers.end(); ++it)
		(*it)->start();
	started.wait(NWORKERS);
// stopping:
	fprintf(stderr, "stop request\n");
	for (it = workers.begin(); it != workers.end(); ++it)
		(*it)->stop();
	for (it = workers.begin(); it != workers.end(); ) {
		if ((*it)->has_exited()) {
	// DEBUG {
			fprintf(stderr, "detected %p exited\n", (*it));
	// DEBUG }
			(*it)->join();
			delete (*it);
			workers.erase(it++);
		} else {
			++it;
		}
	}
}
// DEBUG }

/* Sample output:
worker thread in catchread
worker thread in catchread
worker thread in catchread
stop request
worker thread 0x14de010 exits
worker thread 0x14de1a0 exits
worker thread 0x14de330 exits
detected 0x14de010 exited
detected 0x14de1a0 exited
detected 0x14de330 exited
*/
